package pers.youqu.search.service.impl;

import com.leyou.starter.elastic.entity.PageInfo;
import org.apache.commons.lang3.StringUtils;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.nested.Nested;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import pers.youqu.common.exception.YqException;
import pers.youqu.item.clients.ItemClient;
import pers.youqu.search.dto.SearchParamDTO;
import pers.youqu.search.entity.Goods;
import pers.youqu.search.repository.GoodsRepository;
import pers.youqu.search.service.SearchService;
import reactor.core.publisher.Mono;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author Jason
 * @version 1.0.0
 * @date 2020/12/13 10:18
 * @Description TODO
 */
@Service
public class SearchServiceImpl implements SearchService {
    @Autowired
    private GoodsRepository goodsRepository;

    @Autowired
    private ItemClient itemClient;


    /**
     * 搜索框关键字补全
     *
     * @param key 搜索框输入值
     * @return 可以补全的文本内容
     */
    @Override
    public Mono<List<String>> suggestionKey(String key) {
        if (StringUtils.isBlank(key)) {
            throw new YqException(400, "请求参数不能为空");
        }
        return goodsRepository.suggestBySingleField("suggestion", key);
    }

    /**
     * 搜索SPU商品
     *
     * @param searchParamDTO
     * @return
     */
    @Override
    public Mono<PageInfo<Goods>> pageList(SearchParamDTO searchParamDTO) {

        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();

        // 关键字查询条件
        sourceBuilder.query(getQueryBuilder(searchParamDTO));

        // 分页
        sourceBuilder.from(searchParamDTO.getFrom());
        sourceBuilder.size(searchParamDTO.getSize());

        // 高亮
        sourceBuilder.highlighter(new HighlightBuilder().field("title")
                .preTags("<am>").postTags("</am>"));

        // 排序
        HashSet<String> sortSet = new HashSet<>();
        sortSet.add("prices");
        sortSet.add("sold");
        sortSet.add("updateTime");
        String sort = searchParamDTO.getSortBy();
        if (sort != null && sortSet.contains(sort)) {
            sourceBuilder.sort(sort, searchParamDTO.getDesc() ? SortOrder.DESC : SortOrder.ASC);
        }

        return goodsRepository.queryBySourceBuilderForPageHighlight(sourceBuilder);
    }

    /**
     * 查询条件构造
     *
     * @param searchParamDTO
     * @return
     */
    private QueryBuilder getQueryBuilder(SearchParamDTO searchParamDTO) {

        String key = searchParamDTO.getKey();
        if (StringUtils.isBlank(key)) {
            throw new YqException(400, "搜索条件不能为空！");
        }
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();

        // 使用bool查询过滤
        boolQueryBuilder.must(QueryBuilders.matchQuery("title", key).operator(Operator.AND));

        Map<String, String> filterMap = searchParamDTO.getFilters();
        if (!CollectionUtils.isEmpty(filterMap)) {
            for (Map.Entry<String, String> filterEntry : filterMap.entrySet()) {
                // 过滤条件的key值
                String filterName = filterEntry.getKey();

                // 过滤条件的value值
                String filterValue = filterEntry.getValue();

                // 如果是品牌/分类通过id过滤
                if ("品牌".equals(filterName)) {
                    boolQueryBuilder.filter(QueryBuilders.termQuery("brandId", filterValue));
                } else if ("分类".equals(filterName)) {
                    boolQueryBuilder.filter(QueryBuilders.termQuery("categoryId", filterValue));
                } else {
                    // nested查询
                    boolQueryBuilder.filter(new NestedQueryBuilder("specs",
                            QueryBuilders.boolQuery()
                                    .must(QueryBuilders.termQuery("specs.name", filterName))
                                    .must(QueryBuilders.termQuery("specs.value", filterValue)),
                            ScoreMode.None));
                }
            }
        }

        return boolQueryBuilder;
    }


    /**
     * 过滤SPU商品
     *
     * @param searchParamDTO
     * @return
     */
    @Override
    public Mono<Map<String, List<?>>> findFilters(SearchParamDTO searchParamDTO) {
        // 先查询
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        sourceBuilder.query(getQueryBuilder(searchParamDTO));

        // 聚合查询品牌id和分类id
        sourceBuilder.aggregation(AggregationBuilders.terms("brandAgg").field("brandId").size(30));
        sourceBuilder.aggregation(AggregationBuilders.terms("categoryAgg").field("categoryId").size(30));


        // 添加公有规格参数进行选择过滤
        sourceBuilder.aggregation(AggregationBuilders.nested("specsAgg", "specs")
                .subAggregation(AggregationBuilders.terms("nameAgg").field("specs.name").size(30)
                        .subAggregation(AggregationBuilders.terms("valueAgg").field("specs.value").size(30)))

        );

        sourceBuilder.size(0);


        return goodsRepository.aggregationBySourceBuilder(sourceBuilder)
                .map(aggregation -> {
                    Map<String, List<?>> map = new LinkedHashMap<>();

                    // 获取品牌展示
                    Terms brandAgg = aggregation.get("brandAgg");
                    List<Long> brandIds = brandAgg.getBuckets()
                            .stream()
                            .map(bucket -> ((Terms.Bucket) bucket).getKeyAsNumber())
                            .map(Number::longValue)
                            .collect(Collectors.toList());

                    // 通过brandIds查询品牌
                    if (!CollectionUtils.isEmpty(brandIds)) {
                        map.put("品牌", itemClient.findBrandsByIds(brandIds));
                    }

                    // 获取分类展示
                    Terms categoryAgg = aggregation.get("categoryAgg");
                    List<Long> categoryIds = categoryAgg.getBuckets()
                            .stream()
                            .map(category -> ((Terms.Bucket) category).getKeyAsNumber())
                            .map(Number::longValue)
                            .collect(Collectors.toList());

                    if (!CollectionUtils.isEmpty(categoryIds)) {
                        map.put("分类", itemClient.findCategoryByListId(categoryIds));
                    }

                    // 获取封装规格属性
                    Nested specsAgg = aggregation.get("specsAgg");
                    Terms nameAgg = specsAgg.getAggregations().get("nameAgg");

                    // 封装specs数据
                    nameAgg.getBuckets().forEach(bucket -> {

                        // 获取显示的map的key，name
                        String name = ((Terms.Bucket) bucket).getKeyAsString();

                        // 封装对应的value
                        Terms valueAgg = ((Terms.Bucket) bucket).getAggregations().get("valueAgg");

                        List<String> valueList = valueAgg.getBuckets()
                                .stream()
                                .map(valueBucket -> ((Terms.Bucket) valueBucket).getKeyAsString())
                                .collect(Collectors.toList());

                        map.put(name, valueList);
                    });

                    return map;
                });

    }

}
